#!/usr/bin/env python
# -*- coding: utf-8 -*-
# File  :schedule_task_work.py
# Time  :广州总公司-2025/1/8 17:25
# Email :fortunatedong@163.com
# Author:by Adonli
import schedule
import time
from datetime import datetime, timedelta

class TaskWork:
    def __init__(self, task, start_time, Loop, duration=None, end_time=None, cycle="day"):
        if not task or not callable(task):
            raise ValueError("Task must be a callable function and cannot be None.")
        if not start_time:
            raise ValueError("start_time is required and cannot be None.")
        if Loop is None:
            raise ValueError("Loop is required and cannot be None.")
        if Loop and not duration:
            raise ValueError("duration is required when Loop is True.")

        self.task = task
        self.start_time = self._parse_time(start_time)
        self.Loop = Loop
        self.duration = self._parse_duration(duration) if duration else None
        self.end_time = self._parse_time(end_time) if end_time else None
        self.cycle = cycle

    def _parse_time(self, time_str):
        """Parse time strings like '8:00' into a datetime object."""
        try:
            return datetime.strptime(time_str, "%H:%M").time()
        except ValueError:
            raise ValueError(f"Invalid time format for {time_str}. Use HH:MM format.")

    def _parse_duration(self, duration):
        """Parse duration strings into a timedelta."""
        units = {"s": "seconds", "m": "minutes", "h": "hours", "day": "days", "month": "months"}
        for unit, keyword in units.items():
            if duration.endswith(unit):
                try:
                    value = int(duration[:-len(unit)])
                    if unit == "month":
                        return timedelta(days=value * 30)  # Approximation for months
                    return timedelta(**{keyword: value})
                except ValueError:
                    raise ValueError(f"Invalid duration value: {duration}")
        raise ValueError(f"Unsupported duration format: {duration}")

    def start(self):
        """Start the scheduled task."""
        if self.cycle == "day":
            if self._is_within_time_range():
                self._run_task()
            schedule.every().day.at(self.start_time.strftime("%H:%M")).do(self._run_task)
        else:
            raise ValueError(f"Unsupported cycle: {self.cycle}")

        while True:
            schedule.run_pending()
            time.sleep(1)

    def _is_within_time_range(self):
        """Check if the current time is within the task's execution time range."""
        current_time = datetime.now().time()
        if self.end_time:
            return self.start_time <= current_time <= self.end_time
        else:
            return self.start_time <= current_time

    def _run_task(self):
        """Run the task with the specified conditions."""
        if not self.Loop:
            self.task()
            return schedule.CancelJob  # Stop further executions

        current_time = datetime.now().time()
        next_run_time = datetime.combine(datetime.today(), self.start_time)

        while True:
            if self.end_time and current_time > self.end_time:
                break
            self.task()
            time.sleep(self.duration.total_seconds())
            current_time = (datetime.now() + self.duration).time()
            next_run_time += self.duration

# Example usage:
# def Job():
#     print(f"Task executed at {datetime.now()}.")
#
# scheduled_task = ScheduledTask(task=Job, start_time="8:00", Loop=True, duration="1h", end_time="18:00", cycle="day")
# scheduled_task.start()
